package cz.incad.psh.psh_solr;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 *
 * @author Alberto Hernandez
 */
public class PSHtoSolr {

    private Logger logger = Logger.getLogger(this.getClass().getName());
    ProgramArguments arguments;
    XPathFactory factory = XPathFactory.newInstance();
    XPath xpath;
    XPathExpression expr;
    String xPathStr;
    URL solrUrl;
    Transformer transformer;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
        PSHtoSolr scd = new PSHtoSolr();
        scd.run(args);
    }

    public void run(String[] args) throws Exception {
        this.arguments = new ProgramArguments();
        if (!arguments.parse(args)) {
            throw new Exception("Program arguments are invalid: " + Arrays.toString(args));
        }
        solrUrl = new URL(arguments.solr + "/update");
        if (arguments.clear) {
            clearIndex();
        }
        TransformerFactory tfactory = TransformerFactory.newInstance();
        File xslFile = new File(arguments.xsl);
        if(xslFile.exists()){
            StreamSource xslt = new StreamSource(xslFile);
            transformer = tfactory.newTransformer(xslt);
        }else{
            logger.log(Level.WARNING, "File " + xslFile.getAbsolutePath() + " doesn't exists. Using default.");
            InputStream source = this.getClass().getResourceAsStream("psh.xsl");
            transformer = tfactory.newTransformer(new StreamSource(source));
        }
        
        StreamResult destStream = new StreamResult(new StringWriter());
        StreamSource ss;
        logger.log(Level.INFO, "opening rdf file...");
        if (arguments.rdf.startsWith("http://")) {
            URL url = new URL(arguments.rdf);
            ss = new StreamSource(url.openStream());
        } else {
            File rdf = FileUtils.toFile(new URL(arguments.rdf));
            
            if(rdf.exists()){
                ss = new StreamSource(new FileReader(rdf));
            }else{
                logger.log(Level.WARNING, "File " + xslFile.getAbsolutePath() + " doesn't exists. Using default.");
                InputStream source = this.getClass().getResourceAsStream("psh-skos.rdf");
                ss = new StreamSource(source);
            }
            
            
            
            //ss = new StreamSource(new FileReader(rdf));
        }
        logger.log(Level.INFO, "executing transformation...");
        transformer.transform(ss, destStream);
        StringWriter sw = (StringWriter) destStream.getWriter();

        logger.log(Level.INFO, "posting to solr...");
        postData(new StringReader(sw.toString()), new StringBuilder());
        commit();
    }

    
    public void runParsing(String[] args) throws Exception {

        this.arguments = new ProgramArguments();
        if (!arguments.parse(args)) {
            throw new Exception("Program arguments are invalid: " + Arrays.toString(args));
        }
        solrUrl = new URL(arguments.solr + "/update");
        if (arguments.clear) {
            clearIndex();
        }
        
        logger.log(Level.INFO, "opening rdf file...");
        DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
        df.setNamespaceAware(true);
        DocumentBuilder builder = df.newDocumentBuilder();
        
        
        factory = XPathFactory.newInstance();
        xpath = factory.newXPath();
        xpath.setNamespaceContext(new SKOSNamespaceContext());
            
            
        Document dom;
        if (arguments.rdf.startsWith("http://")) {
            URL url = new URL(arguments.rdf);
            dom = builder.parse(url.openStream());
        } else {
            File rdf = FileUtils.toFile(new URL(arguments.rdf));
            dom = builder.parse(rdf);
        }
        
        
        logger.log(Level.INFO, "parsing rdf file...");
        Document solrDom = builder.newDocument();
        Element root = solrDom.createElement("add");
        solrDom.getDocumentElement().appendChild(root);
        
        xPathStr = "/rdf:RDF/skos:Concept";
        expr = xpath.compile(xPathStr);
        NodeList concepts = (NodeList) expr.evaluate(solrDom, XPathConstants.NODESET);
        for(int i=0; i< concepts.getLength(); i++){
            
        }
    }

    private void commit() throws java.rmi.RemoteException, Exception {
        postData(new StringReader("<commit />"), new StringBuilder());
    }

    private void postData(Reader data, StringBuilder output)
            throws Exception {

        HttpURLConnection urlc = null;
        String POST_ENCODING = "UTF-8";
        try {
            urlc = (HttpURLConnection) solrUrl.openConnection();
            try {
                urlc.setRequestMethod("POST");
            } catch (ProtocolException e) {
                throw new Exception("Shouldn't happen: HttpURLConnection doesn't support POST??", e);
            }
            urlc.setDoOutput(true);
            urlc.setDoInput(true);
            urlc.setUseCaches(false);
            urlc.setAllowUserInteraction(false);
            urlc.setRequestProperty("Content-type", "text/xml; charset=" + POST_ENCODING);

            OutputStream out = urlc.getOutputStream();

            try {
                Writer writer = new OutputStreamWriter(out, POST_ENCODING);
                pipe(data, writer);
                writer.close();
            } catch (IOException e) {
                throw new Exception("IOException while posting data", e);
            } finally {
                if (out != null) {
                    out.close();
                }
            }

            InputStream in = urlc.getInputStream();
            int status = urlc.getResponseCode();
            StringBuilder errorStream = new StringBuilder();
            try {
                if (status != HttpURLConnection.HTTP_OK) {
                    errorStream.append("postData URL=").append(arguments.solr).append(" HTTP response code=").append(status).append(" ");
                    throw new Exception("URL=" + arguments.solr + " HTTP response code=" + status);
                }
                Reader reader = new InputStreamReader(in);
                pipeString(reader, output);
                reader.close();
            } catch (IOException e) {
                throw new Exception("IOException while reading response", e);
            } finally {
                if (in != null) {
                    in.close();
                }
            }

            InputStream es = urlc.getErrorStream();
            if (es != null) {
                try {
                    Reader reader = new InputStreamReader(es);
                    pipeString(reader, errorStream);
                    reader.close();
                } catch (IOException e) {
                    throw new Exception("IOException while reading response", e);
                } finally {
                    if (es != null) {
                        es.close();
                    }
                }
            }
            if (errorStream.length() > 0) {
                throw new Exception("postData error: " + errorStream.toString());
            }

        } catch (IOException e) {
            throw new Exception("Solr has throw an error. Check tomcat log. " + e);
        } finally {
            if (urlc != null) {
                urlc.disconnect();
            }
        }
    }

    /**
     * Pipes everything from the reader to the writer via a buffer
     */
    private static void pipe(Reader reader, Writer writer) throws IOException {
        char[] buf = new char[1024];
        int read = 0;
        while ((read = reader.read(buf)) >= 0) {
            writer.write(buf, 0, read);
        }
        writer.flush();
    }

    /**
     * Pipes everything from the reader to the writer via a buffer except lines
     * starting with '<?'
     */
    private static void pipeString(Reader reader, StringBuilder writer) throws IOException {
        char[] buf = new char[1024];
        int read = 0;
        while ((read = reader.read(buf)) >= 0) {
            if (!(buf[0] == '<' && buf[1] == '?')) {
                writer.append(buf, 0, read);
            }
        }
    }

    private void clearIndex() throws Exception {
        String sb = "<delete><query>*:*</query></delete>";
        logger.log(Level.INFO, "clearing index...");
        postData(new StringReader(sb), new StringBuilder());
    }
}
